#include "tile/emptytile.hpp"

#include "actor.hpp"
#include "game.hpp"
#include "graphics.hpp"
#include "tile/tilemap.hpp"

namespace nmc
{
	EmptyTile::EmptyTile(const Point& tileMapPosition, TileMap* tileMap)
		:Tile(Tile::EMPTY, tileMapPosition, tileMap),
		mLeftEndOfSlope(false),
        mSlopingLeftTilePusher(false)
	{

	}

	void EmptyTile::stickToFloor(Actor* actor, Point& velocity)
	{
		const Point actorBCP = actor->getBottomCollisionPoint();

		if (mLeftEndOfSlope
			&& actorBCP.x > getWorldPosition().x + getWidth() / 2)
		{
			int yCorrection = getWorldPosition().y - actorBCP.y;
			velocity.y = yCorrection - 1;
		}
	}

	void EmptyTile::resolveCollision(Actor* actor, Point& velocity)
	{
        Point actorBCP = actor->getBottomCollisionPoint();

        if (mSlopingLeftTilePusher
            && actorBCP.x + velocity.x > getWorldPosition().x
            && actorBCP.y + velocity.y > getWorldPosition().y
            && actorBCP.x + velocity.x <= getWorldPosition().x + getWidth()
            && actorBCP.y + velocity.y <= getWorldPosition().y + getHeight())
        {
            int yCorrection = ((actorBCP.x + velocity.x) - getWorldPosition().x) - (getWorldPosition().y - (actorBCP.y + velocity.y)) + 1;
            velocity.y = -yCorrection; 
            return;
        }

		if (mLeftEndOfSlope
			&& velocity.y > 0 // Actor moving downwards.
			&& actorBCP.x >= getWorldPosition().x + getWidth() / 2
            && actorBCP.y < getWorldPosition().y // Actor is above the tile
			&& actorBCP.y + velocity.y >= getWorldPosition().y) // Actor will be below the tile
		{
			int yCorrection = getWorldPosition().y - actorBCP.y - 1;
			velocity.y = yCorrection;
			return;
		}
	}

    bool EmptyTile::isStandingOnFloor(Actor* actor)
	{
        const Point actorBCP = actor->getBottomCollisionPoint();

        if (mLeftEndOfSlope)
        {
            return (actor->getPosition().x + actor->getWidth() - 1) >= getWorldPosition().x + getWidth() / 2 - 1
                    && actor->getPosition().x <= getWorldPosition().x + getWidth() - 1
                    && actor->getBottomCollisionPoint().y == getWorldPosition().y - 1;
        }

		return false;
	}

	void EmptyTile::calculateSolidity(TileMap* tileMap)
	{
		Tile* topleft = tileMap->getTile(getTileMapPosition().x + 1, 
									   getTileMapPosition().y - 1);
		Tile* top = tileMap->getTile(getTileMapPosition().x,
			                         getTileMapPosition().y - 1);
    	Tile* left = tileMap->getTile(getTileMapPosition().x - 1, 
									  getTileMapPosition().y);
        
		mLeftEndOfSlope = topleft->getType() == Tile::SLOPING_LEFT
			&& top->getType() == Tile::EMPTY;

        mSlopingLeftTilePusher = (top->getType() == Tile::SLOPING_LEFT)
            && (left->getType() == Tile::SLOPING_LEFT);
	}

	void EmptyTile::draw(Graphics* graphics)
	{

	}

	void EmptyTile::drawType(Graphics* graphics)
	{
        if (mSlopingLeftTilePusher)
        {
            graphics->setColor(Color(0x0000ff));
            graphics->fillRectangle(Rectangle(getWorldPosition().x + Tile::SIZE / 2 - 2,
                                              getWorldPosition().y + Tile::SIZE / 2 - 2,
                                              3,
                                              3));
        }
	}

	void EmptyTile::drawSolidity(Graphics* graphics)
	{
		graphics->setBlendMode(Graphics::ALPHA);
		graphics->setColor(Color(0x00ff00));

		if (mLeftEndOfSlope)
		{
			graphics->drawLine(getWorldPosition().x + Tile::SIZE / 2, 
							   getWorldPosition().y, 
							   getWorldPosition().x + Tile::SIZE - 1,
							   getWorldPosition().y);
		}

		Tile::drawSolidity(graphics);
	}
}